package com.ecr.hub.front.bean.managed;

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.annotation.Resource;

import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Controller;

import com.ecr.hub.ApplicationException;
import com.ecr.hub.CompositionIhmInputException;
import com.ecr.hub.CompositionNotFoundException;
import com.ecr.hub.DestinationUnknownException;
import com.ecr.hub.SillonAlreadyProcededException;
import com.ecr.hub.SillonIhmInputException;
import com.ecr.hub.SillonNotFoundException;
import com.ecr.hub.TcmsTrainIdNotFoundException;
import com.ecr.hub.front.bean.AbstractManagedBean;
import com.ecr.hub.model.composition.Composition;
import com.ecr.hub.model.enumeration.HubLading;
import com.ecr.hub.model.sillon.Sillon;
import com.ecr.hub.model.wagon.Wagon;
import com.ecr.hub.service.data.input.IInputDataManager;
import com.ecr.hub.service.data.input.xls.IXlsDecoder;
import com.ecr.hub.util.MessageUtils;

@Controller("inputDataIhmManaged")
@Scope("session")
public class InputDataIhmManagedBean extends AbstractManagedBean {

	private static final long serialVersionUID = 1L;

	// Liste des sillons pour chargement par IHM.
	private List<Sillon> lstSillons;

	// Liste des compositions pour chargement par IHM.
	private List<Composition> lstCompositions;

	// Le TCMS Train pour rattacher la composition au sillon.
	private String tcmsTrainId;

	// Les listes par défaut.
	private static final int NB_DEFAULT_SILLONS = 10;
	private static final int NB_DEFAULT_COMPOSITIONS = 22;

	@Resource(name = "inputDataManager")
	private transient IInputDataManager inputDataManager;

	/**
	 * Initialisation d'un tableau de saisie pour les sillons. Par défaut on
	 * initialise avec 10 sillons.
	 */
	public void initSillons() {

		lstSillons = new ArrayList<Sillon>();
		for (int i = 0; i < NB_DEFAULT_SILLONS; i++) {
			lstSillons.add(new Sillon());
		}
	}

	/**
	 * Initialisation d'un tableau de saisie pour les compositions. Par défaut,
	 * on initialise avec 22 lignes.
	 */
	public void initCompositions() {

		setTcmsTrainId("");
		lstCompositions = new ArrayList<Composition>();
		for (int i = 0; i < NB_DEFAULT_COMPOSITIONS; i++) {
			lstCompositions.add(new Composition(new Wagon()));
		}
	}

	/**
	 * Méthode très simple pour ajouter une ligne de saisie pour un sillon.
	 */
	public void addSillon() {
		lstSillons.add(new Sillon());
	}

	/**
	 * Méthode très simple pour ajouter une ligne de saisie pour une
	 * composition.
	 */
	public void addComposition() {
		lstCompositions.add(new Composition(new Wagon()));
	}

	/**
	 * Méthode pour l'intégration d'une liste de sillons. On s'arrête
	 * automatiquement dès que l'on ne trouve plus d'identifiant pour un sillon.
	 * On ne passe pas les validateur de jsf pour faire le contrôle de saisie
	 * (contrôles de surface) mais on boucle directement.
	 */
	public void integrateSillons() {

		try {
			// On fait les contrôles de surface et on envoi pour intégration.
			inputDataManager.integrateSillons(checkSillons());
			// On réinitialise.
			initSillons();
			// Message d'accusé.
			MessageUtils.setMessageInfo("sillons.ihm.created");
		} catch (SillonNotFoundException e) {
			MessageUtils.setMessageError("inputdata.ihm.data.required");
		} catch (SillonIhmInputException e) {
			MessageUtils.setMessageError("sillons.ihm.error");
		} catch (ApplicationException e) {
			getErrorHandler(e);
		} catch (Exception e) {
			getErrorHandler(e);
		}
	}

	/**
	 * Ensemble des contrôles de surface pour la liste des sillons à intégrer.
	 * Comme on stoppe à la première ligne ou l'identifiant du sillon n'est pas
	 * trouvé, on en profite pour renvoyer la liste exacte des sillons à
	 * traiter.
	 */
	private List<Sillon> checkSillons() throws SillonIhmInputException,
			SillonNotFoundException {

		boolean bError = false;
		int nbSillonsChecked = 0;

		List<Sillon> lstBaseSillons = new ArrayList<Sillon>();

		// Pattern de vérification pour les heures.
		Pattern p = Pattern.compile(IXlsDecoder.REGEX_TIME);

		for (Sillon sillon : lstSillons) {
			// On réinitialise les champs de contrôle.
			sillon.setErrorMessage("");
			sillon.setErrorIndicator(false);
			Matcher m = p.matcher(sillon.getMovementTime());
			if (sillon.getExtId().trim().length() > 0) {
				// On effecue les tests rapides de surface (champs
				// obligatoires).
				nbSillonsChecked++;
				if (sillon.getTcmsTrainId().trim().length() == 0
						|| sillon.getOrigin().trim().length() == 0
						|| sillon.getMovementDate() == null
						|| sillon.getMovementTime().trim().length() == 0
						|| sillon.getComment().trim().length() == 0
						|| "-1".equals(sillon.getType())) {
					sillon.setErrorMessage(MessageUtils
							.getStringMessageError("inputdata.ihm.required"));
					bError = true;
					// Contrôle sur la validité de l'heure (la validité de la
					// date est déjà effectuée par le composant Jsf).
				} else if (!m.matches()) {
					sillon.setErrorMessage(MessageUtils
							.getStringMessageError("inputdata.ihm.time.format"));
					bError = true;
				}
				// On ajoute le sillon dans la liste finale pour envoi dans la
				// base.
				lstBaseSillons.add(sillon);
			} else {
				break;
			}
		}

		if (nbSillonsChecked == 0)
			throw new SillonNotFoundException("Hub : Aucun sillon à traiter.");

		if (bError)
			throw new SillonIhmInputException(
					"Hub : la liste des sillons est incomplète.");

		return lstBaseSillons;
	}

	/**
	 * Méthode pour l'intégration d'une liste de compositions. On s'arrête
	 * automatiquement dès que l'on ne trouve plus d'identifiant pour un wagon.
	 * On ne passe pas les validateur de jsf pour faire le contrôle de saisie
	 * (contrôles de surface) mais on boucle directement.
	 */
	public void integrateCompositions() {

		try {
			// On fait les contrôles de surface et on envoi pour intégration.
			inputDataManager.integrateCompositions(checkCompositions(),
					getTcmsTrainId());
			// On réinitialise.
			initCompositions();
			// Message d'accusé.
			MessageUtils.setMessageInfo("composition.ihm.created");
		} catch (SillonNotFoundException e) {
			MessageUtils.setMessageError("inputdata.xls.bad.sillon");
		} catch (SillonAlreadyProcededException e) {
			MessageUtils.setMessageError("inputdata.xls.bad.sillon.plan");
		} catch (TcmsTrainIdNotFoundException e) {
			MessageUtils.setMessageError("inputdata.ihm.tcms.required");
		} catch (CompositionNotFoundException e) {
			MessageUtils.setMessageError("inputdata.ihm.data.required");
		} catch (CompositionIhmInputException e) {
			MessageUtils.setMessageError("compositions.ihm.error");
		} catch (DestinationUnknownException e) {
			MessageUtils.setMessageError("compositions.ihm.error");
		} catch (ApplicationException e) {
			getErrorHandler(e);
		} catch (Exception e) {
			getErrorHandler(e);
		}
	}

	/**
	 * Ensemble des contrôles de surface pour la liste des compositions à
	 * intégrer. Comme on stoppe à la première ligne ou l'identifiant du wagon
	 * n'est pas trouvé, on en profite pour renvoyer la liste exacte des
	 * compositions à traiter.
	 */
	private List<Composition> checkCompositions()
			throws CompositionIhmInputException, CompositionNotFoundException,
			TcmsTrainIdNotFoundException {

		boolean bError = false;
		int nbCompositionsChecked = 0;
		List<Composition> lstBaseCompositions = new ArrayList<Composition>();

		for (Composition compo : lstCompositions) {
			// On réinitialise les champs de contrôle.
			compo.setErrorMessage("");
			compo.setErrorIndicator(false);
			if (compo.getWagon().getExtId().trim().length() > 0) {
				nbCompositionsChecked++;

				// On supprime tous les blancs pour la saisie.
				compo.getWagon().setExtId(
						compo.getWagon().getExtId().replaceAll("\\s+", ""));

				// On effecue les tests rapides de surface (champs
				// obligatoires).
				if (compo.getWagon().getExtId().trim().length() == 0
						|| compo.getDestinationExtId().trim().length() == 0
						|| "-1".equals(compo.getWagon().getLading())
						|| "-1".equals(compo.getWagon().getAte())) {
					compo.setErrorMessage(MessageUtils
							.getStringMessageError("inputdata.ihm.required"));
					bError = true;
					// Vérifie la taille du code destination (6).
				} else if (compo.getDestinationExtId().trim().length() != 6) {
					compo.setErrorMessage(MessageUtils
							.getStringMessageError("inputdata.ihm.destination.extid.format"));
					bError = true;
					// Vérifie la taille du code wagon (12).
				} else if (compo.getWagon().getExtId().trim().length() != 12) {
					compo.setErrorMessage(MessageUtils
							.getStringMessageError("inputdata.ihm.wagon.extid.format"));
					bError = true;
					// Vérifie qu'il n'y a pas de poids de chargement si wagon
					// vide.
				} else if (HubLading.EMPTY.getValue().equalsIgnoreCase(
						compo.getWagon().getLading())
						&& compo.getWagon().getWeightLading() > 0) {
					compo.setErrorMessage(MessageUtils
							.getStringMessageError("inputdata.ihm.bad.weight.lading"));
					bError = true;
				}
				// On ajoute la ligne de composition dans la liste finale pour
				// envoi dans la base.
				lstBaseCompositions.add(compo);
			} else {
				break;
			}
		}
		if (nbCompositionsChecked == 0)
			throw new CompositionNotFoundException(
					"Hub : Aucune ligne à traiter.");

		if (bError)
			throw new CompositionIhmInputException(
					"Hub : la liste des compositions est incomplète.");

		if (tcmsTrainId == null || tcmsTrainId.trim().length() == 0)
			throw new TcmsTrainIdNotFoundException(
					"Hub : le train id est obligatoire.");

		return lstBaseCompositions;
	}

	// *********************************
	// Les getters et les setters.
	// *********************************

	public List<Sillon> getLstSillons() {
		return lstSillons;
	}

	public List<Composition> getLstCompositions() {
		return lstCompositions;
	}

	public void setLstSillons(List<Sillon> lstSillons) {
		this.lstSillons = lstSillons;
	}

	public void setLstCompositions(List<Composition> lstCompositions) {
		this.lstCompositions = lstCompositions;
	}

	public String getTcmsTrainId() {
		return tcmsTrainId;
	}

	public void setTcmsTrainId(String tcmsTrainId) {
		this.tcmsTrainId = tcmsTrainId;
	}
}
